//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package testcase_dao

import (
	"context"
	"regexp"

	"atomgit.com/openlinksaas-org/api-server/dao/db_ns"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/project_testcase_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type TestMethodDbItem struct {
	UnitTest   bool `bson:"unitTest"`
	CiTest     bool `bson:"ciTest"`
	LoadTest   bool `bson:"loadTest"`
	ManualTest bool `bson:"manualTest"`
}

type CaseInfoDbItem struct {
	CaseId         string            `bson:"_id"`
	ProjectId      string            `bson:"projectId"`
	Title          string            `bson:"title"`
	ParentFolderId string            `bson:"parentFolderId"`
	PermSetting    PermSettingDbItem `bson:"permSetting"`
	TestMethod     TestMethodDbItem  `bson:"testMethod"`
	CreateUserId   string            `bson:"createUserId"`
	CreateTime     int64             `bson:"createTime"`
	UpdateUserId   string            `bson:"updateUserId"`
	UpdateTime     int64             `bson:"updateTime"`
	ResultCount    uint32            `bson:"resultCount"`
}

func (item *CaseInfoDbItem) ToCaseInfo() *project_testcase_api.CaseInfo {
	return &project_testcase_api.CaseInfo{
		CaseId:         item.CaseId,
		Title:          item.Title,
		ParentFolderId: item.ParentFolderId,
		PermSetting:    item.PermSetting.ToPermSetting(),
		TestMethod: &project_testcase_api.TestMethod{
			UnitTest:   item.TestMethod.UnitTest,
			CiTest:     item.TestMethod.CiTest,
			LoadTest:   item.TestMethod.LoadTest,
			ManualTest: item.TestMethod.ManualTest,
		},
		CreateUserId: item.CreateUserId,
		CreateTime:   item.CreateTime,
		UpdateUserId: item.UpdateUserId,
		UpdateTime:   item.UpdateTime,
		UserPerm:     &project_testcase_api.UserPerm{},
		ResultCount:  item.ResultCount,
	}
}

type _CaseInfoDao struct {
	mc *mongo.Collection
}

func newCaseInfoDao(db *mongo.Database) (*_CaseInfoDao, error) {
	mc := db.Collection(db_ns.DB_NS_PROJECT_TEST_CASE + "case")
	_, err := mc.Indexes().CreateOne(context.Background(), mongo.IndexModel{
		Keys: bson.D{{"projectId", 1}, {"parentFolderId", 1}},
	})
	if err != nil {
		return nil, err
	}
	return &_CaseInfoDao{mc: mc}, nil
}

func (db *_CaseInfoDao) Insert(ctx context.Context, dbItem *CaseInfoDbItem) error {
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_CaseInfoDao) Exist(ctx context.Context, caseId, projectId string) (bool, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": caseId, "projectId": projectId})
	return count > 0, err
}

func (db *_CaseInfoDao) Get(ctx context.Context, caseId, projectId string) (*CaseInfoDbItem, error) {
	retItem := &CaseInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": caseId, "projectId": projectId}).Decode(retItem)
	return retItem, err
}

func (db *_CaseInfoDao) Remove(ctx context.Context, caseId, projectId string) error {
	_, err := db.mc.DeleteOne(ctx, bson.M{"_id": caseId, "projectId": projectId})
	return err
}

func (db *_CaseInfoDao) CountSubCase(ctx context.Context, parentFolderId, projectId string) (uint32, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"projectId": projectId, "parentFolderId": parentFolderId})
	return uint32(count), err
}

func (db *_CaseInfoDao) ListSubCase(ctx context.Context, parentFolderId, projectId string) ([]*CaseInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"projectId": projectId, "parentFolderId": parentFolderId}, options.Find().SetSort(bson.M{"createTime": 1}))
	if err != nil {
		return nil, err
	}
	retList := []*CaseInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_CaseInfoDao) ListById(ctx context.Context, projectId string, caseIdList []string) ([]*CaseInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"projectId": projectId, "_id": bson.M{"$in": caseIdList}}, options.Find().SetSort(bson.M{"createTime": 1}))
	if err != nil {
		return nil, err
	}
	retList := []*CaseInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_CaseInfoDao) ListByProject(ctx context.Context, projectId string) ([]*CaseInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"projectId": projectId}, options.Find().SetSort(bson.M{"createTime": 1}))
	if err != nil {
		return nil, err
	}
	retList := []*CaseInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_CaseInfoDao) genQueryByListParam(projectId string, listParam *project_testcase_api.ListCaseFlatParam) bson.M {
	query := bson.M{"projectId": projectId}
	if listParam.FilterByTitle {
		query["title"] = primitive.Regex{
			Pattern: regexp.QuoteMeta(listParam.Title),
			Options: "i",
		}
	}
	return query
}

func (db *_CaseInfoDao) CountWithParam(ctx context.Context, projectId string, listParam *project_testcase_api.ListCaseFlatParam) (uint32, error) {
	query := db.genQueryByListParam(projectId, listParam)
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_CaseInfoDao) ListWithParam(ctx context.Context, projectId string, listParam *project_testcase_api.ListCaseFlatParam, offset, limit int64) ([]*CaseInfoDbItem, error) {
	query := db.genQueryByListParam(projectId, listParam)
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSkip(offset).SetLimit(limit).SetSort(bson.M{"createTime": -1}))
	if err != nil {
		return nil, err
	}
	retList := []*CaseInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_CaseInfoDao) CountWithParamById(ctx context.Context, projectId string, caseIdList []string, listParam *project_testcase_api.ListCaseFlatParam) (uint32, error) {
	query := db.genQueryByListParam(projectId, listParam)
	query["_id"] = bson.M{"$in": caseIdList}
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_CaseInfoDao) ListWithParamById(ctx context.Context, projectId string, caseIdList []string, listParam *project_testcase_api.ListCaseFlatParam, offset, limit int64) ([]*CaseInfoDbItem, error) {
	query := db.genQueryByListParam(projectId, listParam)
	query["_id"] = bson.M{"$in": caseIdList}
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSkip(offset).SetLimit(limit).SetSort(bson.M{"createTime": -1}))
	if err != nil {
		return nil, err
	}
	retList := []*CaseInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_CaseInfoDao) Update(ctx context.Context, caseId, projectId, title string, testMethod *TestMethodDbItem, updateUserId string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": caseId, "projectId": projectId},
		bson.M{"$set": bson.M{"title": title, "testMethod": testMethod, "updateUserId": updateUserId, "updateTime": updateTime}})
	return err
}

func (db *_CaseInfoDao) UpdatePermSetting(ctx context.Context, caseId, projectId string, permItem *PermSettingDbItem, updateUserId string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": caseId, "projectId": projectId}, bson.M{"$set": bson.M{"permSetting": permItem, "updateUserId": updateUserId, "updateTime": updateTime}})
	return err
}

func (db *_CaseInfoDao) UpdateParentFolder(ctx context.Context, caseId, projectId, parentFolderId, updateUserId string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": caseId, "projectId": projectId}, bson.M{"$set": bson.M{"parentFolderId": parentFolderId, "updateUserId": updateUserId, "updateTime": updateTime}})
	return err
}

func (db *_CaseInfoDao) UpdateResultCount(ctx context.Context, caseId, projectId string, count uint32) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": caseId, "projectId": projectId}, bson.M{"$set": bson.M{"resultCount": count}})
	return err
}

func (db *_CaseInfoDao) DistinctIdByProject(ctx context.Context, projectId string) ([]string, error) {
	tmpList, err := db.mc.Distinct(ctx, "_id", bson.M{"projectId": projectId})
	if err != nil {
		return nil, err
	}
	retList := []string{}
	for _, tmpValue := range tmpList {
		if tmpValue.(string) != "" {
			retList = append(retList, tmpValue.(string))
		}
	}
	return retList, nil
}
